//
// Created by YE on 2024/1/19.
//
#include "executor.h"

typedef void (*MAT_F1)(Float **, Float **, Float **, unsigned int, unsigned int, unsigned int);

typedef void (*MAT_F2)(Float **, Float **, Float, unsigned int, unsigned int, unsigned int);

/*const unsigned int numThreads = std::thread::hardware_concurrency();
std::vector<std::thread> threads = std::vector<std::thread>();*/


static Float **malloc_mat(unsigned int m, unsigned int n) {
    Float **mat = new Float *[m];
    for (unsigned int i = 0; i < m; i++) {
        *(mat + i) = new Float[n];
    }
    return mat;
}

static void delete_mat(Float **mat, Int m) {
    for (unsigned int i = 0; i < m; i++) {
        delete mat[i];
    }
    delete mat;
}

static Mat *copy_mat(Mat *old_m) {
    Mat *new_mat = new Mat({old_m->m, old_m->n});
    new_mat->mat = malloc_mat(new_mat->m, new_mat->n);
    for (unsigned int i = 0; i < old_m->m; i++)
        for (unsigned int j = 0; j < old_m->n; j++)
            new_mat->mat[i][j] = old_m->mat[i][j];
    return new_mat;
}

static void matrix_add(Float **matT, Float **matA, Float **matB, const unsigned int startRow, const unsigned int endRow,
                       const unsigned int COLS) {
    for (unsigned int i = startRow; i < endRow; ++i) {
        for (unsigned int j = 0; j < COLS; ++j) {
            matT[i][j] = matA[i][j] + matB[i][j];
        }
    }
}

static void matrix_sub(Float **matT, Float **matA, Float **matB, const unsigned int startRow, const unsigned int endRow,
                       const unsigned int COLS) {
    for (unsigned int i = startRow; i < endRow; ++i) {
        for (unsigned int j = 0; j < COLS; ++j) {
            matT[i][j] = matA[i][j] - matB[i][j];
        }
    }
}

static void matrix_mul(Float **matT, Float **matA, Float f, const unsigned int startRow, const unsigned int endRow,
                       const unsigned int COLS) {
    for (unsigned int i = startRow; i < endRow; ++i) {
        for (unsigned int j = 0; j < COLS; ++j) {
            matT[i][j] = matA[i][j] * f;
        }
    }
}

static void matrix_div(Float **matT, Float **matA, Float f, const unsigned int startRow, const unsigned int endRow,
                       const unsigned int COLS) {
    for (unsigned int i = startRow; i < endRow; ++i) {
        for (unsigned int j = 0; j < COLS; ++j) {
            matT[i][j] = matA[i][j] / f;
        }
    }
}

static void
matrix_inner(Float **matT, Float **matA, Float **matB, const unsigned int endRow, const unsigned int endCol,
             const unsigned int COLS) {
    for (unsigned int i = 0; i < endRow; ++i) {
        for (unsigned int j = 0; j < endCol; ++j) {
            matT[i][j] = 0;
            for (int k = 0; k < COLS; ++k) {
                matT[i][j] += matA[i][k] * matB[k][j];
            }
        }
    }
}

static void matrix_pow(Float **matT, Float **matA, unsigned int n, const unsigned int N) {
    Float **base1 = malloc_mat(N, N), **base2 = malloc_mat(N, N), **resultT = malloc_mat(N, N), **baseT;
    for (unsigned int i = 0; i < N; i++) {
        for (unsigned int j = 0; j < N; j++) {
            base1[i][j] = matA[i][j];
            if (i == j)
                resultT[i][j] = matT[i][j] = 1;
            else
                resultT[i][j] = matT[i][j] = 0;
        }
    }
    while (n > 0) {
        if (n & 1) {
            matrix_inner(matT, resultT, base1, N, N, N);
        }
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                resultT[i][j] = matT[i][j];
            }
        }
        matrix_inner(base2, base1, base1, N, N, N);
        baseT = base1, base1 = base2, base2 = baseT;
        n >>= 1;
    }
    delete_mat(base1, N);
    delete_mat(base2, N);
    delete_mat(resultT, N);
}

bool matrix_eq(Mat *m1, Mat *m2) {
    if (m1->m != m2->m || m1->n != m2->n) return false;
    Float **mat1 = m1->mat, **mat2 = m2->mat;
    unsigned int m = m1->m, n = m1->n;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (mat1[i][j] != mat2[i][j])
                return false;
        }
    }
    return true;
}


static bool mat_pow(Object *s, Object *o1, Object *o2) {
    if (o1->v.mat->m != o1->v.mat->n || o2->v.i < 0)return false;
    s->v.mat = new Mat({o1->v.mat->m, o1->v.mat->n, malloc_mat(o1->v.mat->m, o1->v.mat->n)});
    matrix_pow(s->v.mat->mat, o1->v.mat->mat, o2->v.i, o1->v.mat->m);
    s->t = Ot_Mat;
    return true;
}


static bool mat_add_sub(Object *s, Object *o1, Object *o2, MAT_F1 func) {
    if (o1->v.mat->m != o2->v.mat->m || o1->v.mat->n != o2->v.mat->n)return false;
    s->v.mat = new Mat({o1->v.mat->m, o1->v.mat->n, malloc_mat(o1->v.mat->m, o1->v.mat->n)});
    /* unsigned rowsPerThread = o1->v.mat->m / numThreads + 1;
     for (unsigned int i = 0; i < o1->v.mat->m; ++i) {
         unsigned int startRow = i * rowsPerThread;
         unsigned int endRow = (startRow + rowsPerThread >= o1->v.mat->m) ? o1->v.mat->m : startRow + rowsPerThread;
         threads.emplace_back(func, s->v.mat->mat, o1->v.mat->mat, o2->v.mat->mat, startRow, endRow,
                              o1->v.mat->n);
     }
     for (auto &thread: threads) {
         thread.join();
     }
     threads.clear();*/
    func(s->v.mat->mat, o1->v.mat->mat, o2->v.mat->mat, 0, o1->v.mat->m,
         o1->v.mat->n);
    s->t = Ot_Mat;
    return true;
}

static bool mat_mulordiv(Object *s, Object *o1, Object *o2, MAT_F2 func) {
    if (o2->t != Ot_Int && o2->t != Ot_Float) return false;
    s->v.mat = new Mat({o1->v.mat->m, o1->v.mat->n, malloc_mat(o1->v.mat->m, o1->v.mat->n)});
/*    unsigned rowsPerThread = o1->v.mat->m / numThreads + 1;
    for (unsigned int i = 0; i < o1->v.mat->m; ++i) {
        unsigned int startRow = i * rowsPerThread;
        unsigned int endRow = (startRow + rowsPerThread >= o1->v.mat->m) ? o1->v.mat->m : startRow + rowsPerThread;
        threads.emplace_back(func, s->v.mat->mat, o1->v.mat->mat, o2->t == Ot_Int ? o2->v.i : o2->v.f, startRow, endRow,
                             o1->v.mat->n);
    }
    for (auto &thread: threads) {
        thread.join();
    }
    threads.clear();*/
    func(s->v.mat->mat, o1->v.mat->mat, o2->t == Ot_Int ? o2->v.i : o2->v.f, 0, o1->v.mat->m,
         o1->v.mat->n);
    s->t = Ot_Mat;
    return true;
}

static bool mat_imul(Object *s, Object *o1, Object *o2) {
    if (o1->v.mat->n != o2->v.mat->m)return false;
    s->v.mat = new Mat({o1->v.mat->m, o2->v.mat->n, malloc_mat(o1->v.mat->m, o2->v.mat->n)});
    /*unsigned rowsPerThread = o1->v.mat->m / numThreads + 1;
    unsigned colsPerThread = o2->v.mat->n / numThreads + 1;
    for (unsigned int i = 0; i < o1->v.mat->m; ++i) {
        unsigned int startRow = i * rowsPerThread;
        unsigned int endRow = (startRow + rowsPerThread >= o1->v.mat->m) ? o1->v.mat->m : startRow + rowsPerThread;
        for (unsigned int j = 0; j < o2->v.mat->n; ++j) {
            unsigned int startCol = j * colsPerThread;
            unsigned int endCol = (startCol + colsPerThread >= o2->v.mat->n) ? o2->v.mat->n : startCol + colsPerThread;
            threads.emplace_back(matrix_inner, s->v.mat->mat, o1->v.mat->mat, o2->v.mat->mat, startRow, endRow,
                                 startCol, endCol,
                                 o1->v.mat->n);
        }
    }
    for (auto &thread: threads) {
        thread.join();
    }
    threads.clear();*/
    matrix_inner(s->v.mat->mat, o1->v.mat->mat, o2->v.mat->mat, o1->v.mat->m, o2->v.mat->n, o1->v.mat->n);
    s->t = Ot_Mat;
    return true;
}

static void gc(Vm_State *vm_state) {
    while (vm_state->gcList) {
        GC_Node *pre = vm_state->gcList->pre;
        delete_mat(vm_state->gcList->mat->mat, vm_state->gcList->mat->m);
        delete vm_state->gcList->mat;
        delete vm_state->gcList;
        vm_state->gcList = pre;
    }
}


Executor_State Executor::execute() const {
    Instruction i;
    unsigned int a, b, c, opcode;
    Ps_State *t;
    Object *oa, *ob, *oc;
    Mat *tm = nullptr;
    while (true) {
        i = *vm_state->pc;
        opcode = GET_OPCODE(i);
        switch (opcode) {
            case OP_LOAD_M:
                a = GET_ARG_A(i), b = GET_ARG_Bx(i);
                VM_R[a] = VM_MEM[b];
                break;
            case OP_LOAD_I:
                a = GET_ARG_A(i), b = GET_ARG_Bx(i);
                VM_R[a].v.i = VM_INT_CONST[b];
                VM_R[a].t = Ot_Int;
                break;
            case OP_LOAD_F:
                a = GET_ARG_A(i), b = GET_ARG_Bx(i);
                VM_R[a].v.f = VM_FLOAT_CONST[b];
                VM_R[a].t = Ot_Float;
                break;
            case OP_LOAD_S:
                a = GET_ARG_A(i);
                VM_R[a] = VM_S[vm_state->stack_top--];
                break;
            case OP_STORE_M:
            case OP_ASSIGN:
                a = GET_ARG_A(i), b = GET_ARG_Bx(i);
                oa = &VM_R[a];
                if (a == 0) {
                    ob = &VM_MEM[b];
                    if (ob->t == oa->t) {
                        if (ob->t == Ot_Mat) {
                            if (ob->v.mat != oa->v.mat) {
                                vm_state->gcList = new GC_Node({vm_state->gcList, ob->v.mat});
                                vm_state->need_gc = true;
                                ob->v.mat = copy_mat(oa->v.mat);
                            }
                        } else {
                            ob->v = oa->v;
                        }
                    } else if (ob->t == Ot_Int && oa->t == Ot_Float) {
                        ob->v.i = oa->v.f;
                    } else if (ob->t == Ot_Float && oa->t == Ot_Int) {
                        ob->v.f = oa->v.i;
                    } else {
                        printf("TYPE ERROR STORE/ASSIGN1\n");
                        return 1;
                    }
                } else {
                    ob = &VM_S[b + vm_state->ps->param_stack_start];
                    if (ob->t == oa->t) {
                        if (ob->t == Ot_Mat) {
                            if (ob->v.mat != oa->v.mat) {
                                vm_state->gcList = new GC_Node({vm_state->gcList, ob->v.mat});
                                vm_state->need_gc = true;
                                ob->v.mat = copy_mat(oa->v.mat);
                            }
                        } else {
                            ob->v = oa->v;
                        }
                    } else if (VM_S[b].t == Ot_Int && oa->t == Ot_Float) {
                        ob->v.i = oa->v.f;
                    } else if (VM_S[b].t == Ot_Float && oa->t == Ot_Int) {
                        ob->v.f = oa->v.i;
                    } else {
                        printf("TYPE ERROR STORE/ASSIGN2\n");
                        return 1;
                    }
                }
                if (opcode == OP_ASSIGN) {
                    VM_S[++vm_state->stack_top] = *oa;
                }
                break;
            case OP_LOAD_B:
                a = GET_ARG_A(i), b = GET_ARG_Bx(i);
                VM_R[a].v.b = b;
                VM_R[a].t = Ot_Bool;
                break;
            case OP_POP:
                if (vm_state->need_gc) {
                    gc(vm_state);
                    vm_state->need_gc = false;
                }
                vm_state->stack_top--;
                break;
            case OP_POPP:
                oa = &VM_S[vm_state->stack_top--];
                if (oa->t == Ot_Mat) {
                    vm_state->gcList = new GC_Node({vm_state->gcList, oa->v.mat});
                }
                break;
            case OP_ADD:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.i = ob->v.i + oc->v.i;
                    oa->t = Ot_Int;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.f = ob->v.f + oc->v.i;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.f = ob->v.i + oc->v.f;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.f = ob->v.f + oc->v.f;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Mat && oc->t == Ot_Mat) {
                    if (!mat_add_sub(oa, ob, oc, &matrix_add))return 1;
                    vm_state->gcList = new GC_Node({vm_state->gcList, oa->v.mat});
                    if (!vm_state->ps)
                        vm_state->need_gc = true;
                } else {
                    printf("TYPE ERROR +\n");
                    return 1;
                }
                break;
            case OP_SUB:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.i = ob->v.i - oc->v.i;
                    oa->t = Ot_Int;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.f = ob->v.f - oc->v.i;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.f = ob->v.i - oc->v.f;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.f = ob->v.f - oc->v.f;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Mat && oc->t == Ot_Mat) {
                    if (!mat_add_sub(oa, ob, oc, &matrix_sub))return 1;
                    vm_state->gcList = new GC_Node({vm_state->gcList, oa->v.mat});
                    if (!vm_state->ps)
                        vm_state->need_gc = true;
                } else {
                    printf("TYPE ERROR -\n");
                    return 1;
                }
                break;
            case OP_MUL:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.i = ob->v.i * oc->v.i;
                    oa->t = Ot_Int;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.f = ob->v.f * oc->v.i;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.f = ob->v.i * oc->v.f;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.f = ob->v.f * oc->v.f;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Mat) {
                    if (!mat_mulordiv(oa, ob, oc, &matrix_mul))return 1;
                    vm_state->gcList = new GC_Node({vm_state->gcList, oa->v.mat});
                    if (!vm_state->ps)
                        vm_state->need_gc = true;
                } else if (oc->t == Ot_Mat) {
                    if (!mat_mulordiv(oa, oc, ob, &matrix_mul))return 1;
                    vm_state->gcList = new GC_Node({vm_state->gcList, oa->v.mat});
                    if (!vm_state->ps)
                        vm_state->need_gc = true;
                } else {
                    printf("TYPE ERROR *\n");
                    return 1;
                }
                break;
            case OP_IMUL:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Mat && oc->t == Ot_Mat) {
                    if (!mat_imul(oa, ob, oc))return 1;
                    vm_state->gcList = new GC_Node({vm_state->gcList, oa->v.mat});
                    if (!vm_state->ps)
                        vm_state->need_gc = true;
                } else {
                    printf("TYPE ERROR .*\n");
                    return 1;
                }
                break;
            case OP_DIV:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.i = ob->v.i / oc->v.i;
                    oa->t = Ot_Int;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.f = ob->v.f / oc->v.i;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.f = ob->v.i / oc->v.f;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.f = ob->v.f / oc->v.f;
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Mat) {
                    if (!mat_mulordiv(oa, ob, oc, &matrix_div))return 1;
                    vm_state->gcList = new GC_Node({vm_state->gcList, oa->v.mat});
                    if (!vm_state->ps)
                        vm_state->need_gc = true;
                } else {
                    printf("TYPE ERROR /\n");
                    return 1;
                }
                break;
            case OP_LESS:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.b = ob->v.i < oc->v.i;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.b = ob->v.f < oc->v.i;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.b = ob->v.i < oc->v.f;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.b = ob->v.f < oc->v.f;
                } else {
                    printf("TYPE ERROR <\n");
                    return 1;
                }
                oa->t = Ot_Bool;
                break;
            case OP_GREATER:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.b = ob->v.i > oc->v.i;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.b = ob->v.f > oc->v.i;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.b = ob->v.i > oc->v.f;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.b = ob->v.f > oc->v.f;
                } else {
                    printf("TYPE ERROR >\n");
                    return 1;
                }
                oa->t = Ot_Bool;
                break;
            case OP_LESSEQUAL:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.b = ob->v.i <= oc->v.i;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.b = ob->v.f <= oc->v.i;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.b = ob->v.i <= oc->v.f;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.b = ob->v.f <= oc->v.f;
                } else {
                    printf("TYPE ERROR <=\n");
                    return 1;
                }
                oa->t = Ot_Bool;
                break;
            case OP_GREATEREQUAL:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.b = ob->v.i >= oc->v.i;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.b = ob->v.f >= oc->v.i;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.b = ob->v.i >= oc->v.f;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.b = ob->v.f >= oc->v.f;
                } else {
                    printf("TYPE ERROR >=\n");
                    return 1;
                }
                oa->t = Ot_Bool;
                break;
            case OP_EQUAL:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.b = ob->v.i == oc->v.i;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.b = ob->v.f == oc->v.i;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.b = ob->v.i == oc->v.f;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.b = ob->v.f == oc->v.f;
                } else if (ob->t == Ot_Mat && oc->t == Ot_Mat) {
                    oa->v.b = matrix_eq(ob->v.mat, oc->v.mat);
                } else {
                    printf("TYPE ERROR ==\n");
                    return 1;
                }
                oa->t = Ot_Bool;
                break;
            case OP_NOTEQUAL:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.b = ob->v.i != oc->v.i;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.b = ob->v.f != oc->v.i;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.b = ob->v.i != oc->v.f;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.b = ob->v.f != oc->v.f;
                } else if (ob->t == Ot_Mat && oc->t == Ot_Mat) {
                    oa->v.b = !matrix_eq(ob->v.mat, oc->v.mat);
                } else {
                    printf("TYPE ERROR !=\n");
                    return 1;
                }
                oa->t = Ot_Bool;
                break;
            case OP_AND:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Bool && oc->t == Ot_Bool) {
                    oa->v.b = ob->v.b && oc->v.b;
                } else {
                    printf("TYPE ERROR &\n");
                    return 1;
                }
                oa->t = Ot_Bool;
                break;
            case OP_OR:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Bool && oc->t == Ot_Bool) {
                    oa->v.b = ob->v.b || oc->v.b;
                } else {
                    printf("TYPE ERROR |\n");
                    return 1;
                }
                oa->t = Ot_Bool;
                break;
            case OP_NOT:
                b = GET_ARG_B(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                if (ob->t == Ot_Bool) {
                    oa->v.b = !ob->v.b;
                    oa->t = Ot_Bool;
                } else {
                    printf("TYPE ERROR !\n");
                    return 1;
                }
                break;
            case OP_NEG:
                b = GET_ARG_B(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                if (ob->t == Ot_Int) {
                    oa->v.i = -ob->v.i;
                    oa->t = Ot_Int;
                } else if (ob->t == Ot_Float) {
                    oa->v.f = -ob->v.f;
                    oa->t = Ot_Float;
                } else {
                    printf("TYPE ERROR neg\n");
                    return 1;
                }
                break;
            case OP_POW:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.i = pow(ob->v.i, oc->v.i);
                    oa->t = Ot_Int;
                } else if (ob->t == Ot_Float && oc->t == Ot_Int) {
                    oa->v.f = pow(ob->v.f, oc->v.i);
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Int && oc->t == Ot_Float) {
                    oa->v.f = pow(ob->v.i, oc->v.f);
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Float && oc->t == Ot_Float) {
                    oa->v.f = pow(ob->v.f, oc->v.f);
                    oa->t = Ot_Float;
                } else if (ob->t == Ot_Mat && oc->t == Ot_Int) {
                    if (!mat_pow(oa, ob, oc))return 1;
                    vm_state->gcList = new GC_Node({vm_state->gcList, oa->v.mat});
                    if (!vm_state->ps)
                        vm_state->need_gc = true;
                } else {
                    printf("TYPE ERROR **\n");
                    return 1;
                }
                break;
            case OP_MOD:
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                if (ob->t == Ot_Int && oc->t == Ot_Int) {
                    oa->v.i = ob->v.i % oc->v.i;
                    oa->t = Ot_Int;
                } else {
                    printf("TYPE ERROR %%\n");
                    return 1;
                }
                break;
            case OP_CJUMPF:
                b = GET_ARG_Bx(i);
                if (!VM_R[a].v.b) vm_state->pc += b;
                break;
            case OP_CJUMPR:
                b = GET_ARG_Bx(i);
                if (!VM_R[a].v.b) vm_state->pc -= b;
                break;
            case OP_JUMP:
                a = GET_ARG_A(i), b = GET_ARG_Bx(i);
                if (a) vm_state->pc += b;
                else vm_state->pc -= b;
                break;
            case OP_MOV:
                a = GET_ARG_A(i);
                switch (a) {
                    case 0:
                    case 1:
                        b = GET_ARG_Bx(i);
                        VM_R[a] = VM_S[vm_state->ps->param_stack_start + b];
                        break;
                    case 2:
                        b = GET_ARG_Bx(i);
                        VM_R[2].v.p = b;
                        break;
                    case 3:
                        VM_R[3] = VM_S[vm_state->stack_top--];
                        break;
                    case 4:
                        b = GET_ARG_B(i), c = GET_ARG_C(i);
                        VM_R[4].v.p = b, VM_R[5].v.p = c;
                        break;
                    case 5:
                        b = GET_ARG_Bx(i);
                        ob = &VM_MEM[b];
                        switch (VM_R[0].t) {
                            case Ot_Int:
                                ob->v.mat->mat[VM_R[4].v.p][VM_R[5].v.p] = VM_R[0].v.i;
                                break;
                            case Ot_Float:
                                ob->v.mat->mat[VM_R[4].v.p][VM_R[5].v.p] = VM_R[0].v.f;
                                break;
                            default:
                                return 1;
                        }
                        break;
                    case 6:
                        b = GET_ARG_Bx(i);
                        ob = &VM_S[vm_state->ps->param_stack_start + b];
                        switch (VM_R[0].t) {
                            case Ot_Int:
                                ob->v.mat->mat[VM_R[4].v.p][VM_R[5].v.p] = VM_R[0].v.i;
                                break;
                            case Ot_Float:
                                ob->v.mat->mat[VM_R[4].v.p][VM_R[5].v.p] = VM_R[0].v.f;
                                break;
                            default:
                                return 1;
                        }
                        break;
                    case 7:
                        VM_R[3] = VM_R[7];
                        break;
                    default:
                        return 1;
                }
                break;
            case OP_CALL:
                a = GET_ARG_A(i), b = GET_ARG_Bx(i);
                vm_state->ps = new Ps_State({vm_state->ps, vm_state->stack_top - b + 1, b, vm_state->pc});
                vm_state->pc = VM_I + VM_R[a].v.p;
                break;
            case OP_PUSH:
                a = GET_ARG_A(i);
                VM_S[++vm_state->stack_top] = VM_R[a];
                break;
            case OP_CALL_C:
                a = GET_ARG_A(i), b = GET_ARG_Bx(i);
                ++vm_state->stack_top;
                switch (a) {
                    case CFT_V0:
                        ((CFV0) VM_C_FUNC[b])();
                        break;
                    case CFT_O1:
                        VM_S[vm_state->stack_top] = ((CFO1) VM_C_FUNC[b])(&VM_S[vm_state->ps->param_stack_start]);
                        break;
                    case CFT_V2:
                        ((CFV2) VM_C_FUNC[b])(&VM_S[vm_state->ps->param_stack_start],
                                              &VM_S[vm_state->ps->param_stack_start + 1]);
                        break;
                    case CFT_O2:
                        VM_S[vm_state->stack_top] = ((CFO2) VM_C_FUNC[b])(&VM_S[vm_state->ps->param_stack_start],
                                                                          &VM_S[vm_state->ps->param_stack_start + 1]);
                        break;
                    case CFT_O3:
                        VM_S[vm_state->stack_top] = ((CFO3) VM_C_FUNC[b])(&VM_S[vm_state->ps->param_stack_start],
                                                                          &VM_S[vm_state->ps->param_stack_start + 1],
                                                                          &VM_S[vm_state->ps->param_stack_start + 2]);
                        break;
                    case CFT_V4:
                        ((CFV4) VM_C_FUNC[b])(&VM_S[vm_state->ps->param_stack_start],
                                              &VM_S[vm_state->ps->param_stack_start + 1],
                                              &VM_S[vm_state->ps->param_stack_start + 2],
                                              &VM_S[vm_state->ps->param_stack_start + 3]);
                        break;
                    case CFT_O4:
                        VM_S[vm_state->stack_top] = ((CFO4) VM_C_FUNC[b])(&VM_S[vm_state->ps->param_stack_start],
                                                                          &VM_S[vm_state->ps->param_stack_start + 1],
                                                                          &VM_S[vm_state->ps->param_stack_start + 2],
                                                                          &VM_S[vm_state->ps->param_stack_start + 3]);
                        if (VM_S[vm_state->stack_top].t == Ot_Mat) {
                            vm_state->gcList = new GC_Node({vm_state->gcList, VM_S[vm_state->stack_top].v.mat});
                        }
                        break;
                    default:
                        return 1;
                }
                break;
            case OP_LEV:
                vm_state->pc = vm_state->ps->return_ip;
                t = vm_state->ps;
                vm_state->ps = vm_state->ps->pre;
                delete t;
                break;
            case OP_MALLOC_MAT:
                a = GET_ARG_A(i), b = GET_ARG_Bx(i);
                if (a) {
                    ob = &VM_S[b + vm_state->ps->param_stack_start];
                    ob->t = Ot_Mat;
                } else {
                    ob = &VM_MEM[b];
                }
                ob->v.mat = new Mat({VM_R[4].v.p, VM_R[5].v.p, malloc_mat(VM_R[4].v.p, VM_R[5].v.p)});
                break;
            case OP_LOAD_STR:
                a = GET_ARG_A(i), b = GET_ARG_Bx(i);
                VM_R[a].v.str= &VM_STR_CONST[b];
                VM_R[a].t = Ot_String;
                break;
            case OP_INDEX:
                b = GET_ARG_B(i), c = GET_ARG_C(i);
                oa = &VM_S[++vm_state->stack_top];
                ob = &VM_R[b];
                oc = &VM_R[c];
                break;
            case OP_GC:
                vm_state->need_gc= true;
                break;
            case OP_END:
                gc(vm_state);
                printf("Finished\n");
                return 1;
            default:
                printf("no this opcode\n");
                return 1;
        }
        vm_state->pc++;
    }

}

Executor::Executor() {
    vm_state = new Vm_State();
    vm_state->pc = VM_I;
    vm_state->need_gc = false;
}

Executor::~Executor() {
    delete vm_state;
}

